using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;

namespace AdobePhotoElementsMetatagger
{
    class ElementsPictureHelper
    {
        private int[] _tagIds = new int[100];

        public int[] TagIds
        {
            get { return _tagIds; }
            set { _tagIds = value; }
        }
        private ElementsPicture _picture;

        internal ElementsPicture Picture
        {
            get { return _picture; }
            set { _picture = value; }
        }

        public ElementsPictureHelper(ElementsDS.ImageTableRow row)
        {
            _picture = new ElementsPicture();
            _picture.FileName = row.fImageOriginalFileName;
            _picture.Path = row.fImageOriginalFilePath;
            _picture.FullFileName = row.fMediaFullPath;
            for (int i = 0; i < 100; i++)
            {
                _tagIds[i] = BitConverter.ToInt32(row.fFolderInfoArray, i * 4);
            }
        }

        internal void UpdateMetaData()
        {
            if (!TypeJpeg() || !FileExists())
            {
                throw new ApplicationException("Not a valid jpeg.");
            }

            //try updating in place (update meta buffer) if that doesnt 
            //work then we will have to create a copy and pad.
            if (!UpdateMetaInPlace())
            {
                UpdateMetaAndCopy();
                Console.WriteLine("Copied");
            }
            else
            {
                Console.WriteLine("In place");
            }
        }

        private void UpdateMetaAndCopy()
        {
            string originalFileName = _picture.FullFileName;
            string finalFile = _picture.FullFileName + ".tmp";
            uint paddingAmount = 2048;

            using (Stream originalStream = new FileStream(originalFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
            {
                //   options for lossless operation.
                BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
                BitmapDecoder original = BitmapDecoder.Create(originalStream, createOptions, BitmapCacheOption.None);

                JpegBitmapEncoder output = new JpegBitmapEncoder();

                //Add Padding
                if (original.Frames[0] != null && original.Frames[0].Metadata != null)
                {
                    BitmapMetadata bitmapMetadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;

                    // pad xmp
                    //bitmapMetadata.SetQuery("/app1/ifd/PaddingSchema:Padding", _MetadataPaddingInBytes);
                    //bitmapMetadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", _MetadataPaddingInBytes);
                    bitmapMetadata.SetQuery("/xmp/PaddingSchema:Padding", paddingAmount);

                    SetMetaInfo(bitmapMetadata);

                    // Create a new frame identical to the one from the original image, except the metadata will have padding.
                    // Essentially we want to keep this as close as possible to:
                    //     output.Frames = original.Frames;
                    output.Frames.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail, bitmapMetadata, original.Frames[0].ColorContexts));
                }

                using (Stream outputStream = File.Open(finalFile, FileMode.Create, FileAccess.ReadWrite))
                {
                    output.Save(outputStream);
                }

            }

            File.Delete(originalFileName);
            File.Move(finalFile, originalFileName);
        }

        private bool UpdateMetaInPlace()
        {
            using (Stream file = File.Open(_picture.FullFileName, FileMode.Open, FileAccess.ReadWrite))
            {
                BitmapDecoder output = BitmapDecoder.Create(file, BitmapCreateOptions.None, BitmapCacheOption.Default);
                InPlaceBitmapMetadataWriter metaDataWriter = output.Frames[0].CreateInPlaceBitmapMetadataWriter();

                SetMetaInfo(metaDataWriter);

                return metaDataWriter.TrySave();
            }
        }

        private void SetMetaInfo(BitmapMetadata bitmapMetadata)
        {
            ReadOnlyCollection<string> keywords = new ReadOnlyCollection<string>(_picture.Keywords);
            bitmapMetadata.Keywords = keywords;
        }

        private bool FileExists()
        {
            FileInfo fi = new FileInfo(_picture.FullFileName);
            return fi.Exists;
        }

        private bool TypeJpeg()
        {
            if (_picture.FullFileName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || _picture.FullFileName.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
            return false;
        }
    }
}
